package edu.del.teamknowledgebase.service.sms.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import edu.del.teamknowledgebase.common.constant.cache.CacheKeyConstant;
import edu.del.teamknowledgebase.common.exception.BusinessException;
import edu.del.teamknowledgebase.common.result.BusinessCode;
import edu.del.teamknowledgebase.config.SmsProperties;
import edu.del.teamknowledgebase.dto.SmsSendRequest;
import edu.del.teamknowledgebase.service.sms.SmsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;


import java.util.concurrent.TimeUnit;

/**
 * 短信服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SmsServiceImpl implements SmsService {

    private final StringRedisTemplate redisTemplate;

    private final SmsProperties smsProperties;

    @Override
    public boolean sendVerificationCode(String phone, String code) {

        // 1. 校验是否可以发送（频率限制）
        if (!canSendCode(phone)) {
            long remainingCooldown = getRemainingCooldown(phone);
            log.warn("验证码发送失败，请等待{}秒后再试", remainingCooldown);
            throw new BusinessException(BusinessCode.CAPTCHA_COOLDOWN, String.format("验证码发送失败，请等待%d秒后再试", remainingCooldown));
        }

        // 2. 构建请求参数
        SmsSendRequest request = new SmsSendRequest();
        request.setCode(code);
        request.setTtl(smsProperties.getCodeExpireMinutes());
        request.setTargets(phone);

        // 3. 发送 post 请求
        String jsonBody = JSONUtil.toJsonStr(request);
        log.info("发送验证码短信到 {}, 请求体: {}", phone, jsonBody);

        String response = HttpUtil.post(smsProperties.getPushUrl(), jsonBody);
        log.info("短信发送响应: {}", response);

        // 4. 记录发送间隔
        recordSendInterval(phone);

        // 5. 增加频率限制计数
        incrementRateLimit(phone);

        // 6. 将验证码存储到 Redis
        String codeKey = String.format(CacheKeyConstant.VERIFICATION_CODE_KEY_PREFIX, phone);
        redisTemplate.opsForValue().set(
                codeKey,
                code,
                smsProperties.getCodeExpireMinutes(),
                TimeUnit.MINUTES
        );
        log.info("验证码已存储到Redis: phone={}, code={}, 有效期={}分钟",
                phone, code, smsProperties.getCodeExpireMinutes());

        return true;
    }

    @Override
    public boolean canSendCode(String phone) {
        // 1. 校验参数
        if(StrUtil.isBlank(phone)) {
            throw new BusinessException(BusinessCode.PARAM_VALID_ERROR, "参数验证失败");
        }

        // 2. 检查发送间隔（冷却时间）
        String intervalKey = String.format(CacheKeyConstant.SMS_INTERVAL_KEY_PREFIX, phone);
        Boolean hasInterval = redisTemplate.hasKey(intervalKey);
        if (Boolean.TRUE.equals(hasInterval)) {
            long remainingCooldown = getRemainingCooldown(phone);
            log.warn("手机号-{}- 发送间隔未到，请稍后再试", phone);
            throw new BusinessException(BusinessCode.SMS_SEND_TOO_SOON,
                    String.format("请等待 %d 秒后重试", remainingCooldown));
        }

        // 3. 检查频率限制（10分钟内发送次数）
        String rateLimitKey = String.format(CacheKeyConstant.SMS_RATE_LIMIT_KEY_PREFIX, phone);
        String countStr = redisTemplate.opsForValue().get(rateLimitKey);
        int currentCount =  countStr != null ? Integer.parseInt(countStr) : 0;

        if (currentCount >= smsProperties.getRateLimitMaxCount()) {
            log.warn("手机号 {} 在 {} 分钟内已发送 {} 次，超过限制", phone, smsProperties.getRateLimitWindowMinutes(), currentCount);
            throw new BusinessException(BusinessCode.SMS_SEND_TOO_FREQUENT,
                    "验证码发送过于频繁，请稍后再试");
        }

        return true;
    }

    @Override
    public long getRemainingCooldown(String phone) {
        String intervalKey = String.format(CacheKeyConstant.SMS_INTERVAL_KEY_PREFIX, phone);
        Long expireTime = redisTemplate.getExpire(intervalKey, TimeUnit.SECONDS);
        return expireTime > 0 ? expireTime : 0;
    }

    /**
     * 记录发送间隔（冷却时间）
     *
     * @param phone 手机号码
     */
    private void recordSendInterval(String phone) {
        String intervalKey = String.format(CacheKeyConstant.SMS_INTERVAL_KEY_PREFIX, phone);
        redisTemplate.opsForValue().set(
                intervalKey,
                "1",
                smsProperties.getSendIntervalMinutes(),
                TimeUnit.MINUTES
        );
        log.info("记录手机号 {} 发送间隔，冷却时间 {} 分钟", phone, smsProperties.getSendIntervalMinutes());
    }

    /**
     * 递增频率限制计数器
     *
     * @param phone 手机号码
     */
    private void incrementRateLimit(String phone) {
        String rateLimitKey = String.format(CacheKeyConstant.SMS_RATE_LIMIT_KEY_PREFIX, phone);
        // 获取当前计数
        String countStr = redisTemplate.opsForValue().get(rateLimitKey);
        if (countStr == null) {
            // 首次发送，设置计数为1，并设置过期时间
            redisTemplate.opsForValue().set(
                    rateLimitKey,
                    "1",
                    smsProperties.getRateLimitWindowMinutes(),
                    TimeUnit.MINUTES
            );
            log.info("初始化手机号 {} 频率限制计数，有效期 {} 分钟", phone, smsProperties.getRateLimitWindowMinutes());
        } else {
            // 增加计数
            redisTemplate.opsForValue().increment(rateLimitKey);
            int newCount = Integer.parseInt(countStr) + 1;
            log.info("手机号 {} 频率限制计数已增加，当前计数为 {}", phone, newCount);
        }
    }

    @Override
    public boolean verifyCode(String receiver, String code) {
       if (StrUtil.isBlank(receiver) || StrUtil.isBlank(code)) {
           throw new BusinessException(BusinessCode.PARAM_VALID_ERROR, "参数验证失败");
       }

        String codeKey = String.format(CacheKeyConstant.VERIFICATION_CODE_KEY_PREFIX, receiver);
       String storedCode = redisTemplate.opsForValue().get(codeKey);

       if (storedCode == null) {
           throw new BusinessException(BusinessCode.VERIFICATION_CODE_NOT_FOUND, "验证码不存在或已过期");
       }

       if (!storedCode.equals(code)) {
           throw new BusinessException(BusinessCode.VERIFICATION_CODE_INVALID, "验证码错误");
       }

       redisTemplate.delete(codeKey);
       log.info("验证码验证成功，手机号 {}", receiver);
       return true;
    }
}
